import os
from pathlib import Path
from typing import Any, Dict, Optional, Union

import yaml


class Config:
    """配置对象，支持通过属性访问配置值"""

    DOMAIN_NAME = "domain.name"
    WEB_DOWNLOADED_DIR = "web_downloaded_dir"
    ARTICLE_DIR = "articledir"
    TEMPLATEDIR = "templatedir"
    ARTICLES_JSON_DIR = "jsondir"
    source_file: str

    def __init__(self, data: Dict[str, Any]):
        self._data = data
        # 递归转换嵌套字典为 Config 对象
        for key, value in data.items():
            if isinstance(value, dict):
                self._data[key] = Config(value)

    def __getattr__(self, name: str) -> Any:
        """通过属性访问配置值
        staticdir 规则：
        - 以 / 或 ~ 开头，按绝对路径处理
        - 其它（如 ./assets、.assets、mystatic），拼接到 articledir 下
        """
        if name in self._data:
            value = self._data[name]
            # web_downloaded_dir 特殊处理
            if name == "web_downloaded_dir" and isinstance(value, str):
                expanded_path = os.path.expanduser(value)
                if os.path.isabs(expanded_path) or value.startswith("~"):
                    return os.path.abspath(expanded_path)
                try:
                    base_dir = os.path.expanduser(self.articledir)
                    expanded_path = os.path.join(base_dir, value)
                except AttributeError:
                    pass
                return os.path.abspath(expanded_path)
            # 其它 dir/file 结尾的属性
            if name.endswith(("dir", "file")) and isinstance(value, str):
                expanded_path = os.path.expanduser(value)
                if name.endswith("file") and not os.path.isabs(expanded_path):
                    try:
                        base_dir = os.path.expanduser(self.articledir)
                        expanded_path = os.path.join(base_dir, expanded_path)
                    except AttributeError:
                        pass
                return os.path.abspath(expanded_path)
            return value
        raise AttributeError(f"'Config' object has no attribute '{name}'")

    def get(self, path: str, default: Any = None) -> Any:
        """通过路径访问配置值，支持嵌套路径，如 'database.host'
        staticdir 规则同上
        """
        parts = path.split(".")
        current = self._data
        for part in parts:
            if part not in current:
                return default
            current = current[part]
            if isinstance(current, Config):
                current = current._data

        # web_downloaded_dir 特殊处理
        if parts[-1] == "web_downloaded_dir" and isinstance(current, str):
            expanded_path = os.path.expanduser(current)
            if os.path.isabs(expanded_path) or current.startswith("~"):
                return os.path.abspath(expanded_path)
            try:
                base_dir = os.path.expanduser(self.articledir)
                expanded_path = os.path.join(base_dir, current)
            except AttributeError:
                pass
            return os.path.abspath(expanded_path)

        # 其它 dir/file 结尾的属性
        if parts[-1].endswith(("dir", "file")) and isinstance(current, str):
            expanded_path = os.path.expanduser(current)
            if parts[-1].endswith("file") and not os.path.isabs(expanded_path):
                try:
                    base_dir = os.path.expanduser(self.articledir)
                    expanded_path = os.path.join(base_dir, expanded_path)
                except AttributeError:
                    pass
            return os.path.abspath(expanded_path)

        return current

    def as_dict(self) -> Dict[str, Any]:
        """将配置转换为字典"""
        result = {}
        for key, value in self._data.items():
            if isinstance(value, Config):
                result[key] = value.as_dict()
            else:
                result[key] = value
        return result


def load_yaml_config(
    file_path: Union[str, Path],
    env_prefix: Optional[str] = None,
    override_with_env: bool = True,
) -> Config:
    """
    加载 YAML 配置文件并返回 Config 对象

    参数:
        file_path: YAML 文件路径
        env_prefix: 环境变量前缀，用于覆盖配置值
        override_with_env: 是否使用环境变量覆盖配置值

    返回:
        Config 对象，支持通过属性访问配置值
    """
    try:
        with open(file_path, "r", encoding="utf-8") as f:
            config_data = yaml.safe_load(f) or {}

        if override_with_env and env_prefix:
            # 使用环境变量覆盖配置值
            for env_key, env_value in os.environ.items():
                if env_key.startswith(env_prefix):
                    # 转换环境变量名到配置路径
                    # 例如: APP_DATABASE_HOST -> database.host
                    config_path = env_key[len(env_prefix) :].lower().replace("_", ".")
                    # 设置配置值
                    _set_nested_value(config_data, config_path, env_value)

        config = Config(config_data)
        config.source_file = str(file_path)
        return config
    except FileNotFoundError as e:
        raise FileNotFoundError(f"Config file not found: {file_path}") from e
    except Exception as e:
        raise ValueError(f"Failed to parse YAML config: {file_path}") from e


def _set_nested_value(data: Dict[str, Any], path: str, value: Any) -> None:
    """设置嵌套字典中的值"""
    parts = path.split(".")
    current = data
    for part in parts[:-1]:
        current = current.setdefault(part, {})
    # 自动转换布尔值和数字
    if isinstance(value, str):
        if value.lower() == "true":
            value = True
        elif value.lower() == "false":
            value = False
        elif value.isdigit():
            value = int(value)
        elif value.replace(".", "", 1).isdigit():
            value = float(value)
    current[parts[-1]] = value
